package com.travelgroup.service.impl;
import cn.dev33.satoken.secure.SaSecureUtil;
import cn.dev33.satoken.stp.StpUtil;
import cn.dev33.satoken.util.SaResult;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.travelgroup.constant.FormatConstant;
import com.travelgroup.domain.dto.LoginDTO;
import com.travelgroup.domain.dto.UserDTO;
import com.travelgroup.domain.entity.User;
import com.travelgroup.exception.BusinessException;
import com.travelgroup.exception.R;
import com.travelgroup.mapper.UserMapper;
import com.travelgroup.service.IUserService;
import com.travelgroup.utils.EmailUtil;
import jakarta.annotation.Resource;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import java.time.LocalDate;
import java.util.*;
import java.util.regex.Pattern;

@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User>
        implements IUserService {
    @Resource
    private EmailUtil emailUtil;

    /**
     * 密码登录
     * @param loginDTO
     * @return R
     */
    @Override
    public R<SaResult> loginByPassword(LoginDTO loginDTO) {
        // 获取用户输入信息
        String email = loginDTO.getEmail();
        String password = loginDTO.getPassword();
        Boolean rememberMe = loginDTO.getRememberMe();

        // 邮箱校验
        if (!emailUtil.checkEmail(email)) {
            return R.error("邮箱格式错误");
        }
        User user = this.lambdaQuery().eq(User::getEmail, email).one();
        if (user == null) {
            return R.error("邮箱未注册");
        }

        // 检查密码是否符合条件
        if (password == null || password.isEmpty()) return R.error("密码不能为空");
        // 检查长度
        if (password.length() < 8 || password.length() > 16) {
            return R.error("密码长度8~16位", 404);
        }
        // 检查密码格式
        if (!Pattern.compile(FormatConstant.REGEX).matcher(password).matches()) {
            return R.error("密码需要同时包含数字、字母和特殊符号中的至少两种类型", 404);
        }
        // 密码加密后检查与数据库中密码是否相同
        password =  SaSecureUtil.md5(FormatConstant.SALT + password);
        if (!password.equals(user.getPassword())) {
            return R.error("密码错误");
        }

        // 校验完毕进行登录并更新用户最后一次登录
        user.setLastLoginTime(LocalDate.now());
        if (!updateById(user)) {
            return R.error("信息更新错误");
        }
        return R.success("登录成功", getResult(user, rememberMe));
    }

    /**
     * 验证码登录
     * @param loginDTO
     * @return
     */
    @Override
    public R<SaResult> loginByCode(LoginDTO loginDTO) {
        // 获取用户输入
        String email = loginDTO.getEmail();
        String code = loginDTO.getCode();
        Boolean rememberMe = loginDTO.getRememberMe();

        // 邮箱校验
        if (!emailUtil.checkEmail(email)) {
            return R.error("邮箱格式错误");
        }

        // 验证码校验
        if (!emailUtil.verifyCode(email, code)) {
            return R.error("无效验证码");
        }
        User user = this.lambdaQuery().eq(User::getEmail, email).one();

        // 邮箱未注册则进行注册
        if (user == null) {
            String password = loginDTO.getPassword();

            // 检查初始密码
            if (password.length() < 8 || password.length() > 16) {
                return R.error("密码长度8~16位", 404);
            }
            if (!Pattern.compile(FormatConstant.REGEX).matcher(password).matches()) {
                return R.error("密码需要同时包含数字、字母和特殊符号中的至少两种类型", 404);
            }

            // 初始化用户数据
            password = SaSecureUtil.md5(FormatConstant.SALT + loginDTO.getPassword());
            user = new User();
            user.setEmail(email);
            user.setPassword(password);
            String initUserName = email.split("@")[0] + UUID.randomUUID().toString().substring(0, 8);
            user.setUsername(initUserName);
        }

        // 校验完毕进行登录并更新用户最后一次登录
        user.setLastLoginTime(LocalDate.now());
        if (!saveOrUpdate(user)) {
            return R.error("信息更新错误");
        }
        return R.success("登录成功", getResult(user, rememberMe));
    }

    /**
     * 获取用户登录结果
     * @param user
     * @param rememberMe
     * @return
     */
    private SaResult getResult(User user, Boolean rememberMe) {
        // 将token等信息传递给前端
        StpUtil.login(user.getId(), rememberMe);
        SaResult result = new SaResult();
        Map<String, Object> map = new HashMap<>();
        map.put(FormatConstant.TOKEN, StpUtil.getTokenValue());
        map.put(FormatConstant.USER_ID, user.getId());
        result.setData(map);
        return result;
    }

    /**
     * 发送验证码
     * @param email
     * @return
     */
    @Override
    public R<String> sendCode(String email) {
        // 检查验证码格式
        if (!emailUtil.checkEmail(email)) {
            return R.error("邮箱格式错误");
        }

        // 将验证码存入缓存
        emailUtil.saveCode(email);
        try {
            // 发送验证码至用户邮箱
            emailUtil.sendVerificationCode(email);
        } catch (Exception e) {
            throw new BusinessException("邮箱发送失败");
        }
        return R.success(null, "验证码发送成功");
    }

    /**
     * 验证码二次校验
     * @param loginDTO
     * @return
     */
    @Override
    public R<String> verifyCode(LoginDTO loginDTO) {
        // 获取用户输入邮箱及验证码
        String email = loginDTO.getEmail();
        String code = loginDTO.getCode();

        // 检查邮箱格式以及验证码
        if (!emailUtil.checkEmail(email)) {
            return R.error("邮箱格式错误");
        }
        // 根据用户邮箱查找用户信息
        User user = this.lambdaQuery().eq(User::getEmail, email).one();
        if (user == null) {
            return R.error("用户不存在");
        }
        if (!emailUtil.verifyCode(email, code)) {
            return R.error("无效验证码");
        }

        // 验证码验证通过，开启二级验证
        StpUtil.openSafe(FormatConstant.VERIFY, FormatConstant.VERIFY_TLL);
        return R.success(null, "认证成功，请在5分钟内完成操作");
    }

    /**
     * 密码二次校验
     * @param loginDTO
     * @return
     */
    @Override
    public R<String> verifyPassword(LoginDTO loginDTO) {
        // 获取用户输入的邮箱及密码
        String email = loginDTO.getEmail();
        String password = loginDTO.getPassword();

        // 进行密码校验
        User user = this.query().eq("email", email).one();
        if (user == null) {
            return R.error("用户不存在");
        }

        // 检查密码
        String checkResult = checkPassword(user, password);
        if (checkResult != null) {
            return R.error(checkResult, 404);
        }

        // 密码验证通过，开启二级验证
        StpUtil.openSafe(FormatConstant.VERIFY, FormatConstant.VERIFY_TLL);
        return R.success(null, "认证成功，请在5分钟内完成操作");
    }

    /**
     * 检查密码
     * @param user
     * @param password
     * @return
     */
    private String checkPassword(User user, String password) {
        // 密码是否为空
        if (password == null || password.isEmpty()) return "密码不能为空";

        // 密码长度是否符合
        if (password.length() < 8 || password.length() > 16) {
            return "密码长度8~16位";
        }

        // 密码格式是否正确
        if (!Pattern.compile(FormatConstant.REGEX).matcher(password).matches()) {
            return "密码需要同时包含数字、字母和特殊符号中的至少两种类型";
        }

        // 密码是否正确
        password =  SaSecureUtil.md5(FormatConstant.SALT + password);
        if (!password.equals(user.getPassword())) {
            return "密码错误";
        }
        return null;
    }

    /**
     * 重置密码
     * @param loginDTO
     * @return
     */
    @Override
    public R<String> resetPassword(LoginDTO loginDTO) {
        // 检查是否进行二级认证
        if (!StpUtil.isSafe(FormatConstant.VERIFY)) {
            return R.error("密码重置失败，请先进行二级认证");
        }

        // 检查密码是否符合条件
        String email = loginDTO.getEmail();
        String password = loginDTO.getPassword();

        // 根据邮箱获取用户信息
        User user = this.query().eq("email", email).one();
        if (user == null) {
            return R.error("用户不存在");
        }
        password = SaSecureUtil.md5(FormatConstant.SALT + password);

        // 更新密码
        user.setPassword(password);
        if (!updateById(user)) {
            return R.error("密码重置失败");
        }
        // 密码重置完成关闭二级认证
        StpUtil.closeSafe(FormatConstant.VERIFY);
        return R.success(null,"密码重置成功");
    }

    /**
     * 更新用户信息
     * @param userDTO
     * @return
     */
    @Override
    public R<String> updateUserInfo(UserDTO userDTO) {
        if (!StpUtil.isLogin()) return R.error("用户未登录");

        // 检查用户名格式
        String username = userDTO.getUsername();
        Long id = StpUtil.getLoginIdAsLong();
        if (username == null || username.isEmpty()) {
            return R.error("用户名不能为空");
        }
        if (username.length() < 3 || username.length() > 20) {
            return R.error("用户名长度不能小于3，大于20");
        }

        // 用户信息更新
        User user = this.getById(id);
        userDTO.setEmail(user.getEmail());
        BeanUtils.copyProperties(userDTO, user);
        if (!updateById(user)) {
            return R.error("用户信息更新失败");
        }
        return R.success(null,  "用户信息修改成功");
    }

    /**
     * 根据用户id查询用户
     * @param id
     * @return
     */
    @Override
    public R<UserDTO> queryById(Long id) {
        // 检查id
        if (id == null) throw new BusinessException("查询用户信息失败");
        User user = this.getById(id);
        // 检查用户是否存在
        if (user == null) {
            return R.error("查询用户不存在");
        }
        // 将用户信息转换为DTO
        UserDTO userDTO = new UserDTO();
        BeanUtils.copyProperties(user, userDTO);
        return R.success("查询用户成功", userDTO);
    }


}
